package cn.vendcommon.com.interceptor;

import cn.vendcommon.com.annotation.RequestLogin;
import cn.vendcommon.com.entity.constants.CommonCodeMsg;
import cn.vendcommon.com.entity.constants.CommonConstants;
import cn.vendcommon.com.entity.Result;
import cn.vendcommon.com.entity.User;
import cn.vendcommon.com.entity.enums.CommonRedisKey;
import cn.vendcommon.com.utils.UserContext;
import com.alibaba.fastjson.JSON;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.List;

public class RequireLoginInterceptor implements HandlerInterceptor {

    private final RedisTemplate redisTemplate;
    private List<String> whiteList;
    public RequireLoginInterceptor(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    public void setWhiteList(List<String> whiteList) {
        this.whiteList = whiteList;
    }

    private boolean isWhiteListed(String path) {
        if (whiteList == null || whiteList.isEmpty()) return false;
        for (String pattern : whiteList) {
            if (pathMatcher.match(pattern, path)) {
                return true;
            }
        }
        return false;
    }


    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
        // 先处理白名单（contextPath 已考虑）
        String uri = request.getRequestURI();
        String context = request.getContextPath();
        String path = uri;
        if (!StringUtils.isEmpty(context) && uri.startsWith(context)) {
            path = uri.substring(context.length());
        }

        // 如果命中白名单则直接放行
        if (isWhiteListed(path)) {
            return true;
        }

        // 如果是 Feign 请求，直接放行
        if ("true".equals(request.getHeader("X-Feign-Request"))) {
            return true;
        }

        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;

            // 如果方法加了 @RequestLogin 注解，则不拦截
            if (handlerMethod.getMethodAnnotation(RequestLogin.class) != null) {
                return true;
            }

            response.setContentType("application/json;charset=utf-8");
            String token = request.getHeader(CommonConstants.TOKEN_NAME);

            // token为空返回“登录超时，请重新登录”
            if (StringUtils.isEmpty(token)) {
                response.getWriter().write(JSON.toJSONString(Result.errorCommonCode(CommonCodeMsg.TOKEN_INVALID)));
                return false;
            }

            User user = JSON.parseObject(
                    (String) redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token)),
                    User.class
            );
            System.out.println(token);
            System.out.println((String) redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token)));

            if (user == null) {
                response.getWriter().write(JSON.toJSONString(Result.errorCommonCode(CommonCodeMsg.TOKEN_INVALID)));
                return false;
            }

            String ip = request.getHeader(CommonConstants.REAL_IP);
            if (!user.getLoginIP().equals(ip)) {
                response.getWriter().write(JSON.toJSONString(Result.errorCommonCode(CommonCodeMsg.LOGIN_IP_CHANGE)));
                return false;
            }
            String redisKey = CommonRedisKey.USER_TOKEN.getRealKey(token);
            String userJson = (String) redisTemplate.opsForValue().get(redisKey);
            // 解析 User 对象
            UserContext.setUserId(Long.valueOf(JSON.parseObject(userJson, User.class).getId()));
        }

        // 非 HandlerMethod 请求直接放行
        return true;
    }
}
